Skip to content

gh-144220: Copy metadata before files in shutil.copytree()#145869

Closed
vstinner wants to merge 1 commit intopython:mainfrom
vstinner:copytree
Closed

gh-144220: Copy metadata before files in shutil.copytree()#145869
vstinner wants to merge 1 commit intopython:mainfrom
vstinner:copytree

Conversation

@vstinner
Copy link
Member

@vstinner vstinner commented Mar 12, 2026

shutil.copytree() now copies directory metadata before copying files. It prevents an error when setting an extended attribute (bcachefs.casefold) on an non-empty directory.

shutil.copytree() now copies directory metadata before copying files.
It prevents an error when setting an extended attribute
(bcachefs.casefold) on an non-empty directory.
@vstinner
Copy link
Member Author

This PR changes st_mtime of the destination directory:

$ mkdir src/ 
$ echo hello >src/hello.txt
$ ./python -c 'import shutil; shutil.copytree("src", "dst")'
$ ./python
>>> import os; src=os.stat("src"); dst=os.stat("dst")
>>> dst.st_mtime_ns == src.st_mtime_ns
False

Without this change, the st_mtime_ns comparison returns True.

@vstinner vstinner marked this pull request as draft March 12, 2026 15:21
@vstinner
Copy link
Member Author

This change doesn't work if the source directory has the UF_IMMUTABLE flag: copytree() fails with PermissionError on copying files to the destination.

Example on FreeBSD 15:

$ mkdir src/
$ echo hello > src/hello.txt
$ ./python -c 'import os, stat; os.chflags("src", stat.UF_IMMUTABLE)'
$ ./python -c 'import shutil; shutil.copytree("src", "dst")'
Traceback (most recent call last):
  File "<string>", line 1, in <module>
    import shutil; shutil.copytree("src", "dst")
                   ~~~~~~~~~~~~~~~^^^^^^^^^^^^^^
  File "/home/vstinner/python/main/Lib/shutil.py", line 656, in copytree
    return _copytree(entries=entries, src=src, dst=dst, symlinks=symlinks,
                     ignore=ignore, copy_function=copy_function,
                     ignore_dangling_symlinks=ignore_dangling_symlinks,
                     dirs_exist_ok=dirs_exist_ok)
  File "/home/vstinner/python/main/Lib/shutil.py", line 610, in _copytree
    raise Error(errors)
shutil.Error: [('src/hello.txt', 'dst/hello.txt', "[Errno 1] Operation not permitted: 'dst/hello.txt'")]

@vstinner
Copy link
Member Author

My PR changes st_mtime and breaks on an UF_IMMUTABLE directory. I prefer to abandon my PR. See the issue for a discussion on how to solve this problem.

@vstinner vstinner closed this Mar 12, 2026
@vstinner vstinner deleted the copytree branch March 12, 2026 17:02
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

needs backport to 3.13 bugs and security fixes needs backport to 3.14 bugs and security fixes

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant